package controllers

import (
    "gouniapp/configs"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/h2non/filetype"
	"github.com/google/uuid"
	"github.com/nfnt/resize"
	"encoding/base64"
	"image"
	"image/jpeg"
	"image/png"
	"io"
	"mime/multipart"
	"net/url"
	"os"
	"path"
	"bytes"
	"path/filepath"
	"regexp"
	"net/http"
	"strconv"
	"crypto/md5"
	"strings"
	"time"
)

var mimeToExtension = map[string]string{
	"image/jpeg": ".jpg",
	"image/png":  ".png",
	"image/gif":  ".gif",
	"image/bmp":  ".bmp",
	"image/tiff": ".tiff",
	"image/webp": ".webp",
}

// 视频MIME类型映射
var videoMimeToExtension = map[string]string{
    "video/mp4":         ".mp4",
    "video/quicktime":   ".mov",
    "video/x-msvideo":   ".avi",
    "video/x-matroska":  ".mkv",
    "video/webm":        ".webm",
}

type MyFileInfo struct {
	Name    string    // 文件基础名称
	Size    int64     // 常规文件的字节长度
	ModTime time.Time // 文件修改时间
	IsDir   bool      // 是否目录
}

type Base64ImageRequest struct {
	Img     string  `json:"img"`    // Base64 图片数据
	Type    string  `json:"type"`   // 图片后缀名
	Path    string  `json:"path"`   //存储路径
}

// 文档MIME类型映射
var documentMimeToExtension = map[string]string{
    "application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx",
    "application/zip": ".docx", // 兼容部分系统返回的 ZIP 类型
}

func init() {
    // 添加 DOCX 类型识别（ZIP文件头特征）
    filetype.AddMatcher(
        filetype.NewType("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
        func(buf []byte) bool {
            return len(buf) > 4 && 
                buf[0] == 0x50 && buf[1] == 0x4B && 
                buf[2] == 0x03 && buf[3] == 0x04
        },
    )
}

// 获取运行目录
func GetExecutableDir() (string, error) {
	executablePath, err := os.Executable()
	if err != nil {
		return "", fmt.Errorf("获取可执行文件路径失败: %v", err)
	}
	return filepath.Dir(executablePath), nil
}

// 上传Word文档
func UploadDocument(c *gin.Context) {
    // 1. 接收并验证文件
    file, err := c.FormFile("file")
    if err != nil {
        JsonResponse(c, http.StatusBadRequest, "无效的文件上传", nil)
        return
    }
    // 2. 读取文件头检测类型
    fileReader, err := file.Open()
    if err != nil {
        JsonResponse(c, http.StatusInternalServerError, "无法读取文件", nil)
        return
    }
    defer fileReader.Close()
    buffer := make([]byte, 512)
    if _, err = fileReader.Read(buffer); err != nil && err != io.EOF {
        JsonResponse(c, http.StatusInternalServerError, "读取文件失败", nil)
        return
    }
    // 3. 验证文档类型
    kind, _ := filetype.Match(buffer)
    ext, ok := documentMimeToExtension[kind.MIME.Value]
    if !ok {
        JsonResponse(c, http.StatusBadRequest, "不支持的文件格式", nil)
        return
    }
    // 4. 创建保存目录
    ctxPath := c.PostForm("path")
    saveDir := filepath.Join("uploads", "wordmb", ctxPath)
    if err := os.MkdirAll(saveDir, os.ModePerm); err != nil {
        JsonResponse(c, http.StatusInternalServerError, 
            fmt.Sprintf("创建目录失败: %v", err), nil)
        return
    }
    // 5. 生成唯一文件名
    fileName := fmt.Sprintf("%d%s", time.Now().UnixNano(), ext)
    savePath := filepath.Join(saveDir, fileName)
    // 6. 保存文件
    if err := c.SaveUploadedFile(file, savePath); err != nil {
        JsonResponse(c, http.StatusInternalServerError, 
            fmt.Sprintf("保存文件失败: %v", err), nil)
        return
    }
    fileurl := buildDocumentURL(ctxPath, fileName)
    JsonResponse(c, 0, fileName, fileurl)
}

//上传视频
func UploadVideo(c *gin.Context) {
    // 1. 接收并验证文件
    file, err := c.FormFile("file")
    if err != nil {
        JsonResponse(c, http.StatusBadRequest, "无效的文件上传", nil)
        return
    }
    // 2. 读取文件头检测类型
    fileReader, err := file.Open()
    if err != nil {
        JsonResponse(c, http.StatusInternalServerError, "无法读取文件", nil)
        return
    }
    defer fileReader.Close()
    buffer := make([]byte, 512)
    if _, err = fileReader.Read(buffer); err != nil && err != io.EOF {
        JsonResponse(c, http.StatusInternalServerError, "读取文件失败", nil)
        return
    }
    // 3. 验证视频类型
    kind, _ := filetype.Match(buffer)
    ext, ok := videoMimeToExtension[kind.MIME.Value]
    if !ok {
        JsonResponse(c, http.StatusBadRequest, "不支持的视频格式", nil)
        return
    }
    // 4. 创建保存目录
    ctxPath := c.PostForm("path")
    saveDir := filepath.Join("uploads", "videos", ctxPath)
    if err := os.MkdirAll(saveDir, os.ModePerm); err != nil {
        JsonResponse(c, http.StatusInternalServerError, 
            fmt.Sprintf("创建目录失败: %v", err), nil)
        return
    }
    // 5. 生成唯一文件名
    fileName := fmt.Sprintf("%d%s", time.Now().UnixNano(), ext)
    savePath := filepath.Join(saveDir, fileName)
    // 6. 保存文件
    if err := c.SaveUploadedFile(file, savePath); err != nil {
        JsonResponse(c, http.StatusInternalServerError, 
            fmt.Sprintf("保存文件失败: %v", err), nil)
        return
    }
    fileurl := buildVideoURL(ctxPath, fileName)
    JsonResponse(c, 0, fileName, fileurl)
}

// 构建视频URL
func buildVideoURL(ctxPath, fileName string) string {
    base := configs.MyURL
    pathSegments := []string{"uploads", "videos"}
    
    if ctxPath != "" {
        pathSegments = append(pathSegments, ctxPath)
    }
    pathSegments = append(pathSegments, fileName)
    
    fullURL, _ := url.JoinPath(base, pathSegments...)
    return fullURL
}

// 构建文档URL
func buildDocumentURL(ctxPath, fileName string) string {
    base := configs.MyURL
    pathSegments := []string{"uploads", "wordmb"}
    if ctxPath != "" {
        pathSegments = append(pathSegments, ctxPath)
    }
    pathSegments = append(pathSegments, fileName)
    fullURL, _ := url.JoinPath(base, pathSegments...)
    return fullURL
}

// 删除图片
func DeleteImage(c *gin.Context) {
	// 1. 参数验证
	idStr := c.Param("id")
	id, err := strconv.Atoi(idStr)
	if err != nil {
		JsonResponse(c, http.StatusBadRequest, "无效的ID", nil)
		return
	}
	// 2. 查询数据库记录
	var fileRecord configs.FileRecord
	if err := configs.DB.First(&fileRecord, id).Error; err != nil {
		JsonResponse(c, http.StatusNotFound, "图片不存在", nil)
		return
	}
	// 3. 删除物理文件
	if err := deletePhysicalFile(fileRecord.FileURL); err != nil {
		JsonResponse(c, http.StatusInternalServerError, err.Error(), nil)
		return
	}
	// 4. 删除数据库记录
	if err := configs.DB.Delete(&fileRecord).Error; err != nil {
		JsonResponse(c, http.StatusInternalServerError, "数据库删除失败", nil)
		return
	}
	JsonResponse(c, http.StatusOK, "删除成功", nil)
}

// 文件删除
func deletePhysicalFile(fileURL string) error {
	parsedURL, err := url.Parse(fileURL)
	if err != nil {
		return fmt.Errorf("图片URL解析失败: %v", err)
	}
	// 获取当前可执行文件所在目录
	executableDir, err := GetExecutableDir()
	if err != nil {
		return err
	}
	// 构造实际文件路径
	filePath := filepath.Join(executableDir, parsedURL.Path)
	filePath = filepath.Clean(filePath) // 清理文件路径
	fmt.Println("准备删除文件：", filePath)
	// 检查文件是否存在
	if _, err := os.Stat(filePath); os.IsNotExist(err) {
		return fmt.Errorf("文件不存在: %v", filePath)
	}
	// 删除文件
	if err := os.Remove(filePath); err != nil {
		return fmt.Errorf("删除文件失败: %v", err)
	}
	return nil
}

// 获取图片列表
func GetImageList(c *gin.Context) {
    var query struct {
        Page     int    `json:"page"`
        PageSize int    `json:"pageSize"`
        Folder   string `json:"folder"` // 必须添加此参数
    }
	if err := c.ShouldBindJSON(&query); err != nil {
		JsonResponse(c, http.StatusBadRequest, "请求参数无效", nil)
		return
	}
	// 限制最大每页数量
	if query.PageSize > 300 {
		query.PageSize = 300
	}
	cx := configs.DB.Model(&configs.FileRecord{}).Order("id DESC")
    if query.Folder != "" {
        cx.Where("folder = ?", query.Folder)
    }
	var total int64
	if err := cx.Count(&total).Error; err != nil {
		JsonResponse(c, http.StatusInternalServerError, "获取总数失败", nil)
		return
	}
	var images []configs.FileRecord
	if err := cx.Scopes(configs.Paginate(query.Page, query.PageSize)).Find(&images).Error; err != nil {
		JsonResponse(c, http.StatusInternalServerError, "获取数据失败", nil)
		return
	}
	JsonResponse(c, http.StatusOK, "获取成功", configs.ImgListResponse{
		Items: images,
		Total: total,
	})
}

// 带数据库记录的上传方法
func UploadWithDB(ctx *gin.Context) {
	// 1. 文件接收验证
	file, err := ctx.FormFile("file")
	if err != nil {
		JsonResponse(ctx, 1, err.Error(), nil)
		return
	}
	folder := strings.TrimSpace(ctx.PostForm("path")) //文件夹
    if !isValidFolder(folder) {
		JsonResponse(ctx, 1, "非法的文件夹名称", nil)
		return
	}
	// 2. 文件类型检测（复用原有逻辑）
	fileReader, err := file.Open()
	if err != nil {
		JsonResponse(ctx, 1, err.Error(), nil)
		return
	}
	defer fileReader.Close()
	buffer := make([]byte, 512)
	if _, err = fileReader.Read(buffer); err != nil && err != io.EOF {
		JsonResponse(ctx, 1, err.Error(), nil)
		return
	}
	// 3. 自定义上传处理
	if filetype.IsImage(buffer) {
		uploadAndSaveToDB(ctx, file, buffer, folder)
	} else {
		JsonResponse(ctx, 1, "仅支持图片文件", nil)
	}
}

func isValidFolder(name string) bool {
    pattern := `^[a-zA-Z0-9_\-\p{Han}]+$` // 使用 Unicode 属性匹配汉字
    matched, _ := regexp.MatchString(pattern, name)
    return len(name) > 0 && len(name) <= 50 && matched
}

// 独立的上传+存储方法
func uploadAndSaveToDB(ctx *gin.Context, file *multipart.FileHeader, buffer []byte, folder string) {
	// A. 文件信息处理（复用Uploadimg逻辑）
	kind, _ := filetype.Match(buffer)
	ext := mimeToExtension[kind.MIME.Value]
	// B. 生成唯一文件名
	fileID := uuid.New().String() // 替换原有时间戳方式
	fname := fmt.Sprintf("%s%s", fileID, ext)
	savePath := filepath.Join("uploads", folder, fname)
	// C. 文件保存（复用核心逻辑）
	if err := ctx.SaveUploadedFile(file, savePath); err != nil {
		JsonResponse(ctx, 1, err.Error(), nil)
		return
	}
	wh := [2]uint{1000, 0} // 默认宽度为1000，长度自动适配
	img, err := CompressImg(savePath, wh)
	if err != nil {
		fmt.Println("压缩失败:", err)
	} else {
		fmt.Println("压缩成功:", img)
		savePath = img
	}
	// D. 创建数据库记录
	fileRecord := configs.FileRecord{
		FileName:  fname,
		FileURL:   buildFileURL(folder, fname), // 使用参数
		Folder:    folder, // 确保设置数据库字段
		CreatedAt: time.Now(),
	}
	if result := configs.DB.Create(&fileRecord); result.Error != nil {
		_ = os.Remove(savePath) // 失败时清理文件
		JsonResponse(ctx, 1, "数据库存储失败", nil)
		return
	}
	// E. 返回结构化数据
	JsonResponse(ctx, 0, "上传成功", gin.H{
		"id":  fileRecord.ID,
		"url": fileRecord.FileURL,
	})
}

// 构建图片方法
func buildFileURL(ctxpath, fname string) string {
	base := configs.MyURL
	if ctxpath != "" {
		return fmt.Sprintf("%suploads/%s/%s", base, ctxpath, fname)
	}
	return fmt.Sprintf("%suploads/%s", base, fname)
}

// Base64 转换为图片并保存的函数
func Base64Toimg(ctx *gin.Context) {
	var request Base64ImageRequest
	if err := ctx.BindJSON(&request); err != nil {
		JsonResponse(ctx, 1, "无效的请求数据", nil)
		return
	}
	base64Data := request.Img
	if base64Data == "" {
		JsonResponse(ctx, 1, "图片数据为空", nil)
		return
	}
	// 去掉 Base64 编码中的头部部分（例如 "data:image/png;base64,"）
	if strings.HasPrefix(base64Data, "data:image") {
		splitData := strings.SplitN(base64Data, ",", 2)
		if len(splitData) == 2 {
			base64Data = splitData[1]
		}
	}
	// 解码 Base64 数据
	imgData, err := base64.StdEncoding.DecodeString(base64Data)
	if err != nil {
		JsonResponse(ctx, 1, "Base64解码失败", nil)
		return
	}
	// 获取图片后缀名
	ext := request.Type
	if ext == "" {
		JsonResponse(ctx, 1, "图片类型为空", nil)
		return
	}
	// 构造文件保存路径
	ctxpath := "images"
	if request.Path != "" {
	    ctxpath = request.Path
	}
	urlpath := "uploads/"
	mypath := urlpath
	if ctxpath != "" {
		urlpath = path.Join("uploads", ctxpath)
		mypath = filepath.Join("uploads", ctxpath)
	}
	// 确保保存目录存在
	if err := os.MkdirAll(mypath, os.ModePerm); err != nil {
		JsonResponse(ctx, 1, err.Error(), nil)
		return
	}
	// 生成随机文件名
	ftype := strings.ToLower(ext)
	fileHash := md5.Sum(imgData)
    fname := fmt.Sprintf("%x.%s", fileHash, ftype) // 基于内容哈希命名
	savePath := filepath.Join(mypath, fname)
	// 保存文件
	err = os.WriteFile(savePath, imgData, 0644)
	if err != nil {
		JsonResponse(ctx, 1, "保存图片失败", nil)
		return
	}
	// 处理图片压缩
	wh := [2]uint{1000, 0} // 默认宽度为1000，长度自动适配
	imgwh := ctx.DefaultPostForm("imgwh", "")
	if imgwh != "" {
		fmt.Println("压缩图片到", imgwh)
		for ii, v := range strings.Split(imgwh, "/") {
			if v == "" {
				continue
			}
			i, _ := strconv.Atoi(v)
			wh[ii] = uint(i)
		}
		img, err := CompressImg(savePath, wh)
		if err != nil {
			fmt.Println("压缩失败:", err)
		} else {
			fmt.Println("压缩成功:", img)
			savePath = img
		}
	}
	// 生成文件URL
	fileurl := path.Join(urlpath, fname)
	Url := configs.MyURL
	fileurl, _ = url.JoinPath(Url, fileurl)
	JsonResponse(ctx, 0, "上传成功", fileurl)
}

// 统一上传入口
func Upload(ctx *gin.Context) {
	file, err := ctx.FormFile("file")
	if err != nil {
		JsonResponse(ctx, 1, err.Error(), nil)
		return
	}
	// 使用 filetype 库来检测文件类型
	fileReader, err := file.Open()
	if err != nil {
		JsonResponse(ctx, 1, err.Error(), nil)
		return
	}
	defer fileReader.Close()
	buffer := make([]byte, 512)
	_, err = fileReader.Read(buffer)
	if err != nil && err != io.EOF {
		fmt.Println("Error reading file:", err)
		JsonResponse(ctx, 1, err.Error(), nil)
		return
	}
    if filetype.IsImage(buffer) {
        Uploadimg(ctx, file, buffer) //图片
    } else if mime, ok := isFontFile(buffer); ok {
        UploadFont(ctx, file, buffer, mime) //字体
    } else {
        JsonResponse(ctx, 1, "不支持的文件类型", nil)
    }
}

// 上传字体
var fontMimeToExtension = map[string]string{
    "font/ttf":    ".ttf",
    "font/otf":    ".otf",
    "font/woff":   ".woff",
    "font/woff2":  ".woff2",
    "application/font-sfnt": ".ttf", // 兼容性处理
}
func isFontFile(buffer []byte) (string, bool) {
    if len(buffer) < 4 {
        return "", false
    }
    // 检测TTF（TrueType）
    if bytes.HasPrefix(buffer, []byte{0x00, 0x01, 0x00, 0x00}) || bytes.HasPrefix(buffer, []byte("true")) {
        return "font/ttf", true
    }
    // 检测OTF（OpenType）
    if bytes.HasPrefix(buffer, []byte("OTTO")) {
        return "font/otf", true
    }
    // 检测WOFF
    if bytes.HasPrefix(buffer, []byte("wOFF")) {
        return "font/woff", true
    }
    // 检测WOFF2
    if bytes.HasPrefix(buffer, []byte("wOF2")) {
        return "font/woff2", true
    }
    return "", false
}
func UploadFont(ctx *gin.Context, file *multipart.FileHeader, buffer []byte, mime string) {
    // 获取扩展名
    ext, ok := fontMimeToExtension[mime]
    if !ok {
        JsonResponse(ctx, 1, "不支持的字体格式", nil)
        return
    }
    // 构造保存路径
    ctxpath := ctx.PostForm("path")
    urlpath := "uploads/"
    mypath := urlpath
    if ctxpath != "" {
        urlpath = path.Join("uploads", ctxpath)
        mypath = filepath.Join("uploads", ctxpath)
    }
    // 创建目录
    if err := os.MkdirAll(mypath, os.ModePerm); err != nil {
        JsonResponse(ctx, 1, err.Error(), nil)
        return
    }
    // 生成随机文件名
    fname := strconv.Itoa(int(time.Now().UnixNano())) + ext
    savePath := filepath.Join(mypath, fname)

    // 保存文件
    if err := ctx.SaveUploadedFile(file, savePath); err != nil {
        JsonResponse(ctx, 1, err.Error(), nil)
        return
    }
    // 生成访问URL
    fileurl := path.Join(urlpath, fname)
    Url := configs.MyURL
    fileurl, _ = url.JoinPath(Url, fileurl)
    JsonResponse(ctx, 0, fname, fileurl)
}

// 上传图片随机命名
func Uploadimg(ctx *gin.Context, file *multipart.FileHeader, buffer []byte) {
	kind, err := filetype.Match(buffer)
	if err != nil {
		JsonResponse(ctx, 1, err.Error(), nil)
		return
	}
	if kind.MIME.Type != "image" {
		fmt.Println("图片格式不支持", kind.MIME.Type)
		JsonResponse(ctx, 1, "图片格式不支持", kind.MIME.Type)
		return
	}
	// 获取扩展名
	ext, ok := mimeToExtension[kind.MIME.Value]
	if !ok {
		JsonResponse(ctx, 1, "不支持的图片格式", nil)
		return
	}
	// 构造文件保存路径
	ctxpath := ctx.PostForm("path")
	urlpath := "uploads/"
	mypath := urlpath
	if ctxpath != "" {
		urlpath = path.Join("uploads", ctxpath)
		mypath = filepath.Join("uploads", ctxpath)
	}
	// 确保保存目录存在
	if err := os.MkdirAll(mypath, os.ModePerm); err != nil {
		JsonResponse(ctx, 1, err.Error(), nil)
		return
	}
	// 生成随机文件名
	ftype := strings.ToLower(ext)
	fname := strconv.Itoa(int(time.Now().UnixNano())) + ftype
	savePath := filepath.Join(mypath, fname)
	// 保存文件
	if err := ctx.SaveUploadedFile(file, savePath); err != nil {
		JsonResponse(ctx, 1, err.Error(), nil)
		return
	}
	// 处理图片压缩
	wh := [2]uint{1000, 0}
	imgwh := ctx.PostForm("imgwh")
	if imgwh != "" {
		fmt.Println("压缩图片到", imgwh)
		for ii, v := range strings.Split(imgwh, "/") {
			if v == "" {
				continue
			}
			i, _ := strconv.Atoi(v)
			wh[ii] = uint(i)
		}
		img, err := CompressImg(savePath, wh)
		if err != nil {
			fmt.Println("压缩失败:", err)
		} else {
			fmt.Println("压缩成功:", img)
			savePath = img
		}
	}
	// 生成文件URL
	fileurl := path.Join(urlpath, fname)
	Url := configs.MyURL
	fileurl, _ = url.JoinPath(Url, fileurl)
	JsonResponse(ctx, 0, fname, fileurl)
}

// 压缩图片指定大小
func CompressImg(fileurl string, imgwh [2]uint) (string, error) {
	var err error
	var file *os.File
	// 检查文件格式
	reg, _ := regexp.Compile(`(?i)^.*\.(png|jpe?g)$`)
	if !reg.MatchString(fileurl) {
		err = errors.New("文件格式不支持，仅支持 .png 或 .jpg 文件")
		fmt.Println(err)
		return "", err
	}
	// 打开文件
	if file, err = os.Open(fileurl); err != nil {
		fmt.Println(err)
		return "", err
	}
	defer file.Close()
	name := file.Name()
	var img image.Image
	width := imgwh[0]
	height := imgwh[1]
	// 解码图片
	switch {
	case strings.HasSuffix(strings.ToLower(name), ".png"):
		if img, err = png.Decode(file); err != nil {
			fmt.Println(err)
			return "", err
		}
	case strings.HasSuffix(strings.ToLower(name), ".jpg"), strings.HasSuffix(strings.ToLower(name), ".jpeg"):
		if img, err = jpeg.Decode(file); err != nil {
			fmt.Println(err)
			return "", err
		}
	default:
		err = fmt.Errorf("图片格式不支持: %s", name)
		fmt.Println(err)
		return "", err
	}
	// 调整图片大小
	resizeImg := resize.Resize(width, height, img, resize.Lanczos3)
	// 保存压缩后的图片
	if outFile, err := os.Create(fileurl); err != nil {
		fmt.Println(err)
		return "", err
	} else {
		defer outFile.Close()
		if strings.HasSuffix(strings.ToLower(name), ".png") {
			err = png.Encode(outFile, resizeImg) // 保存为png，透明
		} else {
			err = jpeg.Encode(outFile, resizeImg, nil)
		}
		if err != nil {
			fmt.Println(err)
			return "", err
		}
	}
	fmt.Printf("压缩后的图片已保存到: %s\n", fileurl)
	return fileurl, nil
}